{
 "cells": [
  {
   "cell_type": "code",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "WKUPkA0TJzbW",
    "outputId": "909eba98-db9c-482f-fb6b-b20b68bce069",
    "ExecuteTime": {
     "end_time": "2025-01-26T01:18:55.229236Z",
     "start_time": "2025-01-26T01:18:47.230149Z"
    }
   },
   "source": [
    "import matplotlib as mpl\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "import numpy as np\n",
    "import sklearn\n",
    "import pandas as pd\n",
    "import os\n",
    "import sys\n",
    "import time\n",
    "from tqdm.auto import tqdm\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "\n",
    "print(sys.version_info)\n",
    "for module in mpl, np, pd, sklearn, torch:\n",
    "    print(module.__name__, module.__version__)\n",
    "\n",
    "device = torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")\n",
    "print(device)\n",
    "\n",
    "seed = 42\n",
    "torch.manual_seed(seed)\n",
    "torch.cuda.manual_seed_all(seed)\n",
    "np.random.seed(seed)\n"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sys.version_info(major=3, minor=12, micro=3, releaselevel='final', serial=0)\n",
      "matplotlib 3.10.0\n",
      "numpy 1.26.4\n",
      "pandas 2.2.3\n",
      "sklearn 1.6.0\n",
      "torch 2.5.1+cpu\n",
      "cpu\n"
     ]
    }
   ],
   "execution_count": 1
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "pSNcMyqvJzbY"
   },
   "source": [
    "## 数据加载"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "Dm1DrSCLJzbZ",
    "outputId": "ab7f8ef9-6119-4d2b-df23-7cc04f617802",
    "ExecuteTime": {
     "end_time": "2025-01-26T01:18:55.507802Z",
     "start_time": "2025-01-26T01:18:55.230239Z"
    }
   },
   "source": [
    "import unicodedata\n",
    "import re\n",
    "from sklearn.model_selection import train_test_split\n",
    "\n",
    "#因为西班牙语有一些是特殊字符，所以我们需要unicode转ascii，\n",
    "# 这样值变小了，因为unicode太大\n",
    "def unicode_to_ascii(s):\n",
    "    #NFD是转换方法，把每一个字节拆开，Mn是重音，所以去除\n",
    "    return ''.join(c for c in unicodedata.normalize('NFD', s) if unicodedata.category(c) != 'Mn')\n",
    "\n",
    "#下面我们找个样本测试一下\n",
    "# 加u代表对字符串进行unicode编码\n",
    "en_sentence = u\"May I borrow this book?\"\n",
    "sp_sentence = u\"¿Puedo tomar prestado este libro?\"\n",
    "\n",
    "print(unicode_to_ascii(en_sentence))\n",
    "print(unicode_to_ascii(sp_sentence))\n",
    "\n",
    "\n"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "May I borrow this book?\n",
      "¿Puedo tomar prestado este libro?\n"
     ]
    }
   ],
   "execution_count": 2
  },
  {
   "cell_type": "code",
   "source": [
    "def preprocess_sentence(w):\n",
    "    #变为小写，去掉多余的空格，变成小写，id少一些\n",
    "    w = unicode_to_ascii(w.lower().strip())\n",
    "\n",
    "    # 在单词与跟在其后的标点符号之间插入一个空格\n",
    "    # eg: \"he is a boy.\" => \"he is a boy . \"\n",
    "    # Reference:- https://stackoverflow.com/questions/3645931/python-padding-punctuation-with-white-spaces-keeping-punctuation\n",
    "    w = re.sub(r\"([?.!,¿])\", r\" \\1 \", w)\n",
    "    #因为可能有多余空格，替换为一个空格，所以处理一下\n",
    "    w = re.sub(r'[\" \"]+', \" \", w)\n",
    "\n",
    "    # 除了 (a-z, A-Z, \".\", \"?\", \"!\", \",\")，将所有字符替换为空格，你可以保留一些标点符号\n",
    "    w = re.sub(r\"[^a-zA-Z?.!,¿]+\", \" \", w)\n",
    "\n",
    "    w = w.rstrip().strip()\n",
    "\n",
    "    return w\n",
    "\n",
    "print(preprocess_sentence(en_sentence))\n",
    "print(preprocess_sentence(sp_sentence))\n",
    "print(preprocess_sentence(sp_sentence).encode('utf-8'))  #¿是占用两个字节的"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-01-26T01:18:55.512466Z",
     "start_time": "2025-01-26T01:18:55.507802Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "may i borrow this book ?\n",
      "¿ puedo tomar prestado este libro ?\n",
      "b'\\xc2\\xbf puedo tomar prestado este libro ?'\n"
     ]
    }
   ],
   "execution_count": 3
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "YyJksrNmJzba"
   },
   "source": [
    "Dataset"
   ]
  },
  {
   "cell_type": "code",
   "source": [
    "#zip例子\n",
    "a = [[1,2],[4,5],[7,8]]\n",
    "zipped = list(zip(*a))\n",
    "print(zipped)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-01-26T01:18:55.515501Z",
     "start_time": "2025-01-26T01:18:55.512466Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[(1, 4, 7), (2, 5, 8)]\n"
     ]
    }
   ],
   "execution_count": 4
  },
  {
   "cell_type": "code",
   "source": [
    "split_index1 = np.random.choice(a=[\"train\", \"test\"], replace=True, p=[0.9, 0.1], size=100)\n",
    "split_index1"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-01-26T01:18:55.519731Z",
     "start_time": "2025-01-26T01:18:55.515501Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array(['train', 'test', 'train', 'train', 'train', 'train', 'train',\n",
       "       'train', 'train', 'train', 'train', 'test', 'train', 'train',\n",
       "       'train', 'train', 'train', 'train', 'train', 'train', 'train',\n",
       "       'train', 'train', 'train', 'train', 'train', 'train', 'train',\n",
       "       'train', 'train', 'train', 'train', 'train', 'test', 'test',\n",
       "       'train', 'train', 'train', 'train', 'train', 'train', 'train',\n",
       "       'train', 'test', 'train', 'train', 'train', 'train', 'train',\n",
       "       'train', 'test', 'train', 'test', 'train', 'train', 'test',\n",
       "       'train', 'train', 'train', 'train', 'train', 'train', 'train',\n",
       "       'train', 'train', 'train', 'train', 'train', 'train', 'test',\n",
       "       'train', 'train', 'train', 'train', 'train', 'train', 'train',\n",
       "       'train', 'train', 'train', 'train', 'train', 'train', 'train',\n",
       "       'train', 'train', 'train', 'train', 'train', 'train', 'train',\n",
       "       'train', 'train', 'train', 'train', 'train', 'train', 'train',\n",
       "       'train', 'train'], dtype='<U5')"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 5
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "-VnoIKhaJzba",
    "ExecuteTime": {
     "end_time": "2025-01-26T01:18:55.748289Z",
     "start_time": "2025-01-26T01:18:55.519731Z"
    }
   },
   "source": [
    "from pathlib import Path\n",
    "from torch.utils.data import Dataset, DataLoader\n",
    "\n",
    "class LangPairDataset(Dataset):\n",
    "    fpath = Path(r\"./data_spa_en/spa.txt\") #数据文件路径\n",
    "    cache_path = Path(r\"./.cache/lang_pair.npy\") #缓存文件路径\n",
    "    split_index = np.random.choice(a=[\"train\", \"test\"], replace=True, p=[0.9, 0.1], size=118964) #按照9:1划分训练集和测试集\n",
    "    def __init__(self, mode=\"train\", cache=False):\n",
    "        if cache or not self.cache_path.exists():#如果没有缓存，或者缓存不存在，就处理一下数据\n",
    "            self.cache_path.parent.mkdir(parents=True, exist_ok=True) #创建缓存文件夹，如果存在就忽略\n",
    "            with open(self.fpath, \"r\", encoding=\"utf8\") as file:\n",
    "                lines = file.readlines()\n",
    "                lang_pair = [[preprocess_sentence(w) for w in l.split('\\t')]  for l in lines] #处理数据，变成list((trg, src))的形式\n",
    "                trg, src = zip(*lang_pair) #分离出目标语言和源语言\n",
    "                trg=np.array(trg) #转换为numpy数组\n",
    "                src=np.array(src) #转换为numpy数组\n",
    "                np.save(self.cache_path, {\"trg\": trg, \"src\": src})  #保存为npy文件,方便下次直接读取,不用再处理\n",
    "        else:\n",
    "            lang_pair = np.load(self.cache_path, allow_pickle=True).item() #读取npy文件，allow_pickle=True允许读取字典\n",
    "            trg = lang_pair[\"trg\"]\n",
    "            src = lang_pair[\"src\"]\n",
    "\n",
    "        self.trg = trg[self.split_index == mode] #按照index拿到训练集的 标签语言 --英语\n",
    "        self.src = src[self.split_index == mode] #按照index拿到训练集的源语言 --西班牙\n",
    "\n",
    "    def __getitem__(self, index):\n",
    "        return self.src[index], self.trg[index]\n",
    "\n",
    "    def __len__(self):\n",
    "        return len(self.src)\n",
    "\n",
    "\n",
    "train_ds = LangPairDataset(\"train\")\n",
    "test_ds = LangPairDataset(\"test\")"
   ],
   "outputs": [],
   "execution_count": 6
  },
  {
   "cell_type": "code",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "knue-PUkJzbb",
    "outputId": "86c1d3c8-8bd2-4c7f-8576-7516c4767ec2",
    "ExecuteTime": {
     "end_time": "2025-01-26T01:18:55.751325Z",
     "start_time": "2025-01-26T01:18:55.748289Z"
    }
   },
   "source": "print(\"source: {}\\ntarget: {}\".format(*train_ds[-1]))",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "source: si quieres sonar como un hablante nativo , debes estar dispuesto a practicar diciendo la misma frase una y otra vez de la misma manera en que un musico de banjo practica el mismo fraseo una y otra vez hasta que lo puedan tocar correctamente y en el tiempo esperado .\n",
      "target: if you want to sound like a native speaker , you must be willing to practice saying the same sentence over and over in the same way that banjo players practice the same phrase over and over until they can play it correctly and at the desired tempo .\n"
     ]
    }
   ],
   "execution_count": 7
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "9mzBlPtGJzbb"
   },
   "source": [
    "### Tokenizer\n",
    "\n",
    "这里有两种处理方式，分别对应着 encoder 和 decoder 的 word embedding 是否共享，这里实现不共享的方案。"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "fMSIczSnJzbb",
    "ExecuteTime": {
     "end_time": "2025-01-26T01:19:28.900803Z",
     "start_time": "2025-01-26T01:19:28.575045Z"
    }
   },
   "source": [
    "from collections import Counter\n",
    "\n",
    "def get_word_idx(ds, mode=\"src\", threshold=2):\n",
    "    #载入词表，看下词表长度，词表就像英语字典\n",
    "    word2idx = {\n",
    "        \"[PAD]\": 0,     # 填充 token\n",
    "        \"[BOS]\": 1,     # begin of sentence\n",
    "        \"[UNK]\": 2,     # 未知 token\n",
    "        \"[EOS]\": 3,     # end of sentence\n",
    "    }\n",
    "    idx2word = {value: key for key, value in word2idx.items()}\n",
    "    index = len(idx2word)\n",
    "    threshold = 1  # 出现次数低于此的token舍弃\n",
    "    #如果数据集有很多个G，那是用for循环的，不能' '.join\n",
    "    word_list = \" \".join([pair[0 if mode==\"src\" else 1] for pair in ds]).split()\n",
    "    # print(type(word_list))\n",
    "    counter = Counter(word_list) #统计词频,counter类似字典，key是单词，value是出现次数\n",
    "    print(\"word count:\", len(counter))\n",
    "\n",
    "    for token, count in counter.items():\n",
    "        if count >= threshold:#出现次数大于阈值的token加入词表\n",
    "            word2idx[token] = index #加入词表\n",
    "            idx2word[index] = token #加入反向词表\n",
    "            index += 1\n",
    "\n",
    "    return word2idx, idx2word\n",
    "\n",
    "src_word2idx, src_idx2word = get_word_idx(train_ds, \"src\") #源语言词表  西班牙语\n",
    "trg_word2idx, trg_idx2word = get_word_idx(train_ds, \"trg\") #目标语言词表 英语"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "word count: 23715\n",
      "word count: 12500\n"
     ]
    }
   ],
   "execution_count": 9
  },
  {
   "cell_type": "code",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "9_IjY_wIJzbb",
    "outputId": "f2bf8be3-ec47-48e2-b743-1d2dbd511adc",
    "ExecuteTime": {
     "end_time": "2025-01-26T01:29:52.990182Z",
     "start_time": "2025-01-26T01:29:52.958504Z"
    }
   },
   "source": [
    "class Tokenizer:\n",
    "    def __init__(self, word2idx, idx2word, max_length=500, pad_idx=0, bos_idx=1, eos_idx=3, unk_idx=2):\n",
    "        self.word2idx = word2idx\n",
    "        self.idx2word = idx2word\n",
    "        self.max_length = max_length\n",
    "        self.pad_idx = pad_idx\n",
    "        self.bos_idx = bos_idx\n",
    "        self.eos_idx = eos_idx\n",
    "        self.unk_idx = unk_idx\n",
    "\n",
    "    def encode(self, text_list, padding_first=False, add_bos=True, add_eos=True, return_mask=False):\n",
    "        \"\"\"如果padding_first == True，则padding加载前面，否则加载后面\n",
    "        return_mask: 是否返回mask(掩码），mask用于指示哪些是padding的，哪些是真实的token\n",
    "        \"\"\"\n",
    "        max_length = min(self.max_length, add_eos + add_bos + max([len(text) for text in text_list]))\n",
    "        indices_list = []\n",
    "        for text in text_list:\n",
    "            indices = [self.word2idx.get(word, self.unk_idx) for word in text[:max_length - add_bos - add_eos]] #如果词表中没有这个词，就用unk_idx代替，indices是一个list,里面是每个词的index,也就是一个样本的index\n",
    "            if add_bos:\n",
    "                indices = [self.bos_idx] + indices\n",
    "            if add_eos:\n",
    "                indices = indices + [self.eos_idx]\n",
    "            if padding_first:#padding加载前面，超参可以调\n",
    "                indices = [self.pad_idx] * (max_length - len(indices)) + indices\n",
    "            else:#padding加载后面\n",
    "                indices = indices + [self.pad_idx] * (max_length - len(indices))\n",
    "            indices_list.append(indices)\n",
    "        input_ids = torch.tensor(indices_list) #转换为tensor\n",
    "        masks = (input_ids == self.pad_idx).to(dtype=torch.int64) #mask是一个和input_ids一样大小的tensor，0代表token，1代表padding，mask用于去除padding的影响\n",
    "        return input_ids if not return_mask else (input_ids, masks)\n",
    "\n",
    "\n",
    "    def decode(self, indices_list, remove_bos=True, remove_eos=True, remove_pad=True, split=False):\n",
    "        text_list = []\n",
    "        for indices in indices_list:\n",
    "            text = []\n",
    "            for index in indices:\n",
    "                word = self.idx2word.get(index, \"[UNK]\") #如果词表中没有这个词，就用unk_idx代替\n",
    "                if remove_bos and word == \"[BOS]\":\n",
    "                    continue\n",
    "                if remove_eos and word == \"[EOS]\":#如果到达eos，就结束\n",
    "                    break\n",
    "                if remove_pad and word == \"[PAD]\":#如果到达pad，就结束\n",
    "                    break\n",
    "                text.append(word) #单词添加到列表中\n",
    "            text_list.append(\" \".join(text) if not split else text) #把列表中的单词拼接，变为一个句子\n",
    "        return text_list\n",
    "\n",
    "#两个相对于1个toknizer的好处是embedding的参数量减少\n",
    "src_tokenizer = Tokenizer(word2idx=src_word2idx, idx2word=src_idx2word) #源语言tokenizer\n",
    "trg_tokenizer = Tokenizer(word2idx=trg_word2idx, idx2word=trg_idx2word) #目标语言tokenizer\n",
    "\n",
    "# trg_tokenizer.encode([[\"hello\"], [\"hello\", \"world\"]], add_bos=True, add_eos=False,return_mask=True)\n",
    "raw_text = [\"hello world\".split(), \"tokenize text datas with batch\".split(), \"this is a test\".split()]\n",
    "indices,mask = trg_tokenizer.encode(raw_text, padding_first=False, add_bos=True, add_eos=True,return_mask=True)\n",
    "\n",
    "print(\"raw text\"+'-'*10)\n",
    "for raw in raw_text:\n",
    "    print(raw)\n",
    "print(\"mask\"+'-'*10)\n",
    "for m in mask:\n",
    "    print(m)\n",
    "print(\"indices\"+'-'*10)\n",
    "for index in indices:\n",
    "    print(index)\n"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "raw text----------\n",
      "['hello', 'world']\n",
      "['tokenize', 'text', 'datas', 'with', 'batch']\n",
      "['this', 'is', 'a', 'test']\n",
      "mask----------\n",
      "tensor([0, 0, 0, 0, 1, 1, 1])\n",
      "tensor([0, 0, 0, 0, 0, 0, 0])\n",
      "tensor([0, 0, 0, 0, 0, 0, 1])\n",
      "indices----------\n",
      "tensor([   1,   16, 3218,    3,    0,    0,    0])\n",
      "tensor([   1,    2, 3878,    2,  552,    2,    3])\n",
      "tensor([   1,  117,  235,  103, 2896,    3,    0])\n"
     ]
    }
   ],
   "execution_count": 10
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-26T01:36:30.727994Z",
     "start_time": "2025-01-26T01:36:30.724471Z"
    }
   },
   "cell_type": "code",
   "source": [
    "decode_text = trg_tokenizer.decode(indices.tolist(), remove_bos=False, remove_eos=False, remove_pad=False)\n",
    "print(\"decode text\"+'-'*10)\n",
    "for decode in decode_text:\n",
    "    print(decode)"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "decode text----------\n",
      "[BOS] hello world [EOS] [PAD] [PAD] [PAD]\n",
      "[BOS] [UNK] text [UNK] with [UNK] [EOS]\n",
      "[BOS] this is a test [EOS] [PAD]\n"
     ]
    }
   ],
   "execution_count": 11
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "S8BDjaa1Jzbc"
   },
   "source": [
    "### DataLoader"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "sPwlGzn8Jzbc",
    "ExecuteTime": {
     "end_time": "2025-01-26T01:53:44.896967Z",
     "start_time": "2025-01-26T01:53:44.892965Z"
    }
   },
   "source": [
    "def collate_fct(batch):\n",
    "    src_words = [pair[0].split() for pair in batch] #取batch内第0列进行分词，赋给src_words\n",
    "    trg_words = [pair[1].split() for pair in batch] #取batch内第1列进行分词，赋给trg_words\n",
    "\n",
    "    # [PAD] [BOS] src [EOS]\n",
    "    encoder_inputs, encoder_inputs_mask = src_tokenizer.encode(\n",
    "        src_words, padding_first=True, add_bos=True, add_eos=True, return_mask=True\n",
    "        )\n",
    "\n",
    "    # [BOS] trg [PAD]\n",
    "    decoder_inputs = trg_tokenizer.encode(\n",
    "        trg_words, padding_first=False, add_bos=True, add_eos=False, return_mask=False,\n",
    "        )\n",
    "\n",
    "    # trg [EOS] [PAD]\n",
    "    decoder_labels, decoder_labels_mask = trg_tokenizer.encode(\n",
    "        trg_words, padding_first=False, add_bos=False, add_eos=True, return_mask=True\n",
    "        )\n",
    "\n",
    "    return {\n",
    "        \"encoder_inputs\": encoder_inputs.to(device=device),\n",
    "        \"encoder_inputs_mask\": encoder_inputs_mask.to(device=device),\n",
    "        \"decoder_inputs\": decoder_inputs.to(device=device),\n",
    "        \"decoder_labels\": decoder_labels.to(device=device),\n",
    "        \"decoder_labels_mask\": decoder_labels_mask.to(device=device), #mask用于去除padding的影响，计算loss时用\n",
    "    } #当返回的数据较多时，用dict返回比较合理\n"
   ],
   "outputs": [],
   "execution_count": 12
  },
  {
   "cell_type": "code",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "_JsuutYAJzbc",
    "outputId": "fd68e596-ed01-4bae-d731-c27f5a758a6c",
    "ExecuteTime": {
     "end_time": "2025-01-26T01:55:18.277289Z",
     "start_time": "2025-01-26T01:55:18.271794Z"
    }
   },
   "source": [
    "sample_dl = DataLoader(train_ds, batch_size=2, shuffle=True, collate_fn=collate_fct)\n",
    "\n",
    "#两次执行这个代码效果不一样，因为每次执行都会shuffle\n",
    "for batch in sample_dl:\n",
    "    for key, value in batch.items():\n",
    "        print(key)\n",
    "        print(value)\n",
    "    break"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "encoder_inputs\n",
      "tensor([[   0,    1,  537,   67, 4517,   91, 1071,   37,  827,    5,    3],\n",
      "        [   1,  202,  153,  484,   79,   82, 3158,   67, 1549,    5,    3]])\n",
      "encoder_inputs_mask\n",
      "tensor([[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])\n",
      "decoder_inputs\n",
      "tensor([[   1,   17,  449,  299,  945,   82,   17,  403,    5],\n",
      "        [   1,   47,  235,  263,   58,  689, 2832, 1186,    5]])\n",
      "decoder_labels\n",
      "tensor([[  17,  449,  299,  945,   82,   17,  403,    5,    3],\n",
      "        [  47,  235,  263,   58,  689, 2832, 1186,    5,    3]])\n",
      "decoder_labels_mask\n",
      "tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 0]])\n"
     ]
    }
   ],
   "execution_count": 14
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "K9JaKLR7Jzbc"
   },
   "source": [
    "## 定义模型"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "CGxzT605Jzbd",
    "ExecuteTime": {
     "end_time": "2025-01-26T02:32:59.897354Z",
     "start_time": "2025-01-26T02:32:59.894441Z"
    }
   },
   "source": [
    "class Encoder(nn.Module):\n",
    "    def __init__(\n",
    "        self,\n",
    "        vocab_size,\n",
    "        embedding_dim=256,\n",
    "        hidden_dim=1024,\n",
    "        num_layers=1,\n",
    "        ):\n",
    "        super().__init__()\n",
    "        self.embedding = nn.Embedding(vocab_size, embedding_dim)\n",
    "        self.gru = nn.GRU(embedding_dim, hidden_dim, num_layers=num_layers, batch_first=True)\n",
    "\n",
    "    def forward(self, encoder_inputs):\n",
    "        # encoder_inputs.shape = [batch size, sequence length]\n",
    "        # bs, seq_len = encoder_inputs.shape\n",
    "        embeds = self.embedding(encoder_inputs)\n",
    "        # embeds.shape = [batch size, sequence length, embedding_dim]->[batch size, sequence length, hidden_dim]\n",
    "        seq_output, hidden = self.gru(embeds)\n",
    "        # seq_output.shape = [batch size, sequence length, hidden_dim]，hidden.shape [ num_layers, batch size, hidden_dim]\n",
    "        return seq_output, hidden"
   ],
   "outputs": [],
   "execution_count": 16
  },
  {
   "cell_type": "code",
   "source": [
    "#把上面的Encoder写一个例子，看看输出的shape\n",
    "encoder = Encoder(vocab_size=100, embedding_dim=256, hidden_dim=1024, num_layers=4)\n",
    "encoder_inputs = torch.randint(0, 100, (2, 50))\n",
    "encoder_outputs, hidden = encoder(encoder_inputs)\n",
    "print(encoder_outputs.shape)\n",
    "print(hidden.shape)\n",
    "print(encoder_outputs[:,-1,:])\n",
    "print(hidden[-1,:,:]) #取最后一层的hidden"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-01-26T02:34:16.644968Z",
     "start_time": "2025-01-26T02:34:16.538182Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([2, 50, 1024])\n",
      "torch.Size([4, 2, 1024])\n",
      "tensor([[-0.0147, -0.0008, -0.0222,  ...,  0.0161,  0.0803, -0.0508],\n",
      "        [-0.0258, -0.0052, -0.0526,  ...,  0.0663,  0.1045, -0.0602]],\n",
      "       grad_fn=<SliceBackward0>)\n",
      "tensor([[-0.0147, -0.0008, -0.0222,  ...,  0.0161,  0.0803, -0.0508],\n",
      "        [-0.0258, -0.0052, -0.0526,  ...,  0.0663,  0.1045, -0.0602]],\n",
      "       grad_fn=<SliceBackward0>)\n"
     ]
    }
   ],
   "execution_count": 20
  },
  {
   "cell_type": "code",
   "source": [
    "query1 = torch.randn(2, 1024)\n",
    "query1.unsqueeze(1).shape #增加维度"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-01-26T02:46:42.630426Z",
     "start_time": "2025-01-26T02:46:42.626342Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "torch.Size([2, 1, 1024])"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 21
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "## BahdanauAttention公式\n",
    "score = FC(tanh(FC(EO) + FC(H))) #FC(EO)的FC是Wk,FC(H)的FC是Wq,最外面的FC是V \n",
    "\n",
    "attention_weights = softmax(score, axis = 1)  \n",
    "\n",
    "context = sum(attention_weights * EO, axis = 1) #对EO做加权求和，得到上下文向量"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "pTQ6Mz1OJzbd",
    "ExecuteTime": {
     "end_time": "2025-01-26T03:11:14.836454Z",
     "start_time": "2025-01-26T03:11:14.831950Z"
    }
   },
   "source": [
    "class BahdanauAttention(nn.Module):\n",
    "    def __init__(self, hidden_dim=1024):\n",
    "        super().__init__()\n",
    "        self.Wk = nn.Linear(hidden_dim, hidden_dim) #对keys做运算，encoder的输出EO\n",
    "        self.Wq = nn.Linear(hidden_dim, hidden_dim) #对query做运算，decoder的隐藏状态\n",
    "        self.V = nn.Linear(hidden_dim, 1)\n",
    "\n",
    "    def forward(self, query, keys, values, attn_mask=None):\n",
    "        \"\"\"\n",
    "        正向传播\n",
    "        :param query: hidden state，是decoder的隐藏状态，shape = [batch size, hidden_dim]\n",
    "        :param keys: EO  [batch size, sequence length, hidden_dim]\n",
    "        :param values: EO  [batch size, sequence length, hidden_dim]\n",
    "        :param attn_mask:[batch size, sequence length],这里是encoder_inputs_mask\n",
    "        :return:\n",
    "        \"\"\"\n",
    "        # query.shape = [batch size, hidden_dim] -->通过unsqueeze(-2)增加维度 [batch size, 1, hidden_dim]\n",
    "        # keys.shape = [batch size, sequence length, hidden_dim]\n",
    "        # values.shape = [batch size, sequence length, hidden_dim]\n",
    "        scores = self.V(F.tanh(self.Wk(keys) + self.Wq(query.unsqueeze(-2)))) #unsqueeze(-2)增加维度\n",
    "        # score.shape = [batch size, sequence length, 1]\n",
    "        if attn_mask is not None: #这个mask是encoder_inputs_mask，用来mask掉padding的部分,让padding部分socres为0\n",
    "            # attn_mask is a matrix of 0/1 element,\n",
    "            # 1 means to mask logits while 0 means do nothing\n",
    "            # here we add -inf to the element while mask == 1\n",
    "            attn_mask = (attn_mask.unsqueeze(-1)) * -1e16 #在最后增加一个维度，[batch size, sequence length] --> [batch size, sequence length, 1]\n",
    "            scores += attn_mask\n",
    "        scores = F.softmax(scores, dim=-2) #对每一个词的score做softmax\n",
    "        # score.shape = [batch size, sequence length, 1]\n",
    "        context_vector = torch.mul(scores, values).sum(dim=-2) #对每一个词的score和对应的value做乘法，然后在seq_len维度上求和，得到context_vector\n",
    "        # context_vector.shape = [batch size, hidden_dim]\n",
    "        #socres用于最后的画图\n",
    "        return context_vector, scores\n"
   ],
   "outputs": [],
   "execution_count": 23
  },
  {
   "cell_type": "code",
   "source": [
    "#tensor矩阵相乘\n",
    "a = torch.randn(2, 3)\n",
    "b = torch.randn(2, 3)\n",
    "c = torch.mul(a, b) #增加维度\n",
    "print(c.shape)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-01-26T03:12:54.122497Z",
     "start_time": "2025-01-26T03:12:54.119147Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([2, 3])\n"
     ]
    }
   ],
   "execution_count": 27
  },
  {
   "cell_type": "code",
   "source": [
    "#把上面的BahdanauAttention写一个例子，看看输出的shape\n",
    "attention = BahdanauAttention(hidden_dim=1024)\n",
    "query = torch.randn(2, 1024) #Decoder的隐藏状态\n",
    "keys = torch.randn(2, 50, 1024) #EO\n",
    "values = torch.randn(2, 50, 1024) #EO\n",
    "attn_mask = torch.randint(0, 2, (2, 50))\n",
    "context_vector, scores = attention(query, keys, values, attn_mask)\n",
    "print(context_vector.shape)\n",
    "print(scores.shape)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-01-26T03:11:17.240095Z",
     "start_time": "2025-01-26T03:11:17.222613Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([2, 1024])\n",
      "torch.Size([2, 50, 1])\n"
     ]
    }
   ],
   "execution_count": 24
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "6W5FeRRrJzbd",
    "ExecuteTime": {
     "end_time": "2025-01-26T06:24:35.447479Z",
     "start_time": "2025-01-26T06:24:35.429034Z"
    }
   },
   "source": [
    "class Decoder(nn.Module):\n",
    "    def __init__(\n",
    "        self,\n",
    "        vocab_size,\n",
    "        embedding_dim=256,\n",
    "        hidden_dim=1024,\n",
    "        num_layers=1,\n",
    "        ):\n",
    "        super(Decoder, self).__init__()\n",
    "        self.embedding = nn.Embedding(vocab_size, embedding_dim)\n",
    "        self.gru = nn.GRU(embedding_dim + hidden_dim, hidden_dim, num_layers=num_layers, batch_first=True)\n",
    "        self.fc = nn.Linear(hidden_dim, vocab_size) #最后分类,词典大小是多少，就输出多少个分类\n",
    "        self.dropout = nn.Dropout(0.6)\n",
    "        self.attention = BahdanauAttention(hidden_dim) #注意力得到的context_vector\n",
    "\n",
    "    def forward(self, decoder_input, hidden, encoder_outputs, attn_mask=None):\n",
    "        #attn_mask是encoder_inputs_mask\n",
    "        # decoder_input.shape = [batch size, 1]\n",
    "        assert len(decoder_input.shape) == 2 and decoder_input.shape[-1] == 1, f\"decoder_input.shape = {decoder_input.shape} is not valid\"\n",
    "        # hidden.shape = [batch size, hidden_dim]，decoder_hidden,而第一次使用的是encoder的hidden\n",
    "        assert len(hidden.shape) == 2, f\"hidden.shape = {hidden.shape} is not valid\"\n",
    "        # encoder_outputs.shape = [batch size, sequence length, hidden_dim]\n",
    "        assert len(encoder_outputs.shape) == 3, f\"encoder_outputs.shape = {encoder_outputs.shape} is not valid\"\n",
    "        # context_vector.shape = [batch_size, hidden_dim]\n",
    "        context_vector, attention_score = self.attention(\n",
    "            query=hidden, keys=encoder_outputs, values=encoder_outputs, attn_mask=attn_mask)\n",
    "        # decoder_input.shape = [batch size, 1]\n",
    "        embeds = self.embedding(decoder_input)\n",
    "        # embeds.shape = [batch size, 1, embedding_dim]\n",
    "        # context_vector.shape = [batch size, hidden_dim] -->unsqueeze(-2)增加维度 [batch size, 1, hidden_dim]\n",
    "        embeds = torch.cat([context_vector.unsqueeze(-2), embeds], dim=-1)\n",
    "        # 新的embeds.shape = [batch size, 1, embedding_dim + hidden_dim]\n",
    "        seq_output, hidden = self.gru(embeds)\n",
    "        # seq_output.shape = [batch size, 1, hidden_dim]\n",
    "        logits = self.fc(self.dropout(seq_output))\n",
    "        # logits.shape = [batch size, 1, vocab size]，attention_score = [batch size, sequence length, 1]\n",
    "        return logits, hidden, attention_score\n",
    "\n"
   ],
   "outputs": [],
   "execution_count": 28
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "FG-Pid9cJzbd",
    "ExecuteTime": {
     "end_time": "2025-01-26T06:53:07.851633Z",
     "start_time": "2025-01-26T06:53:07.843470Z"
    }
   },
   "source": [
    "class Sequence2Sequence(nn.Module):\n",
    "    def __init__(\n",
    "        self,\n",
    "        src_vocab_size, #输入词典大小\n",
    "        trg_vocab_size, #输出词典大小\n",
    "        encoder_embedding_dim=256,\n",
    "        encoder_hidden_dim=1024, #encoder_hidden_dim和decoder_hidden_dim必须相同，是因为BahdanauAttention设计的\n",
    "        encoder_num_layers=1,\n",
    "        decoder_embedding_dim=256,\n",
    "        decoder_hidden_dim=1024,\n",
    "        decoder_num_layers=1,\n",
    "        bos_idx=1,\n",
    "        eos_idx=3,\n",
    "        max_length=512,\n",
    "        ):\n",
    "        super(Sequence2Sequence, self).__init__()\n",
    "        self.bos_idx = bos_idx\n",
    "        self.eos_idx = eos_idx\n",
    "        self.max_length = max_length\n",
    "        self.encoder = Encoder(\n",
    "            src_vocab_size,\n",
    "            embedding_dim=encoder_embedding_dim,\n",
    "            hidden_dim=encoder_hidden_dim,\n",
    "            num_layers=encoder_num_layers,\n",
    "            )\n",
    "        self.decoder = Decoder(\n",
    "            trg_vocab_size,\n",
    "            embedding_dim=decoder_embedding_dim,\n",
    "            hidden_dim=decoder_hidden_dim,\n",
    "            num_layers=decoder_num_layers,\n",
    "            )\n",
    "\n",
    "    def forward(self, *, encoder_inputs, decoder_inputs, attn_mask=None):\n",
    "        # encoding\n",
    "        encoder_outputs, hidden = self.encoder(encoder_inputs)\n",
    "        # decoding with teacher forcing\n",
    "        bs, seq_len = decoder_inputs.shape\n",
    "        logits_list = []\n",
    "        scores_list = []\n",
    "        for i in range(seq_len):#串行训练\n",
    "            # 每次迭代生成一个时间步的预测，存储在 logits_list 中，并且记录注意力分数（如果有的话）在 scores_list 中，最后将预测的logits和注意力分数拼接并返回。\n",
    "            logits, hidden, score = self.decoder(\n",
    "                decoder_inputs[:, i:i+1],\n",
    "                hidden[-1], #取最后一层的hidden，第一个时间步时，hidden是encoder的hidden，第二个时间步时，hidden是decoder的hidden\n",
    "                encoder_outputs,\n",
    "                attn_mask=attn_mask\n",
    "                )\n",
    "            logits_list.append(logits) #记录预测的logits，用于计算损失\n",
    "            scores_list.append(score) #记录注意力分数,用于画图\n",
    "\n",
    "        return torch.cat(logits_list, dim=-2), torch.cat(scores_list, dim=-1)\n",
    "\n",
    "    @torch.no_grad() #不计算梯度\n",
    "    def infer(self, encoder_input, attn_mask=None):\n",
    "        #infer用于预测\n",
    "        # encoder_input.shape = [1, sequence length],这只支持batch_size=1\n",
    "        # encoding\n",
    "        encoder_outputs, hidden = self.encoder(encoder_input)\n",
    "\n",
    "        # decoding，[[1]]\n",
    "        decoder_input = torch.Tensor([self.bos_idx]).reshape(1, 1).to(dtype=torch.int64) #shape为[1,1]，内容为开始标记\n",
    "        decoder_pred = None\n",
    "        pred_list = [] #预测序列\n",
    "        score_list = []\n",
    "        # 从开始标记 bos_idx 开始，迭代地生成序列，直到生成结束标记 eos_idx 或达到最大长度 max_length。\n",
    "        for _ in range(self.max_length):\n",
    "            logits, hidden, score = self.decoder(\n",
    "                decoder_input,\n",
    "                hidden[-1],\n",
    "                encoder_outputs,\n",
    "                attn_mask=attn_mask\n",
    "                )\n",
    "            # using greedy search,logits shape = [1, 1, vocab size]\n",
    "            decoder_pred = logits.argmax(dim=-1)\n",
    "            decoder_input = decoder_pred\n",
    "            pred_list.append(decoder_pred.reshape(-1).item()) #decoder_pred从(1,1)变为（1）标量\n",
    "            score_list.append(score) #记录注意力分数,用于画图\n",
    "\n",
    "            # stop at eos token\n",
    "            if decoder_pred == self.eos_idx:\n",
    "                break\n",
    "\n",
    "        # return\n",
    "        return pred_list, torch.cat(score_list, dim=-1)\n",
    "\n"
   ],
   "outputs": [],
   "execution_count": 29
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-26T06:56:14.335739Z",
     "start_time": "2025-01-26T06:56:13.950283Z"
    }
   },
   "cell_type": "code",
   "source": [
    "model = Sequence2Sequence(src_vocab_size=len(src_word2idx), trg_vocab_size=len(trg_word2idx))\n",
    "#做model的前向传播，看看输出的shape\n",
    "encoder_inputs = torch.randint(0, 100, (2, 50))\n",
    "decoder_inputs = torch.randint(0, 100, (2, 50))\n",
    "attn_mask = torch.randint(0, 2, (2, 50))\n",
    "logits, scores = model(encoder_inputs=encoder_inputs, decoder_inputs=decoder_inputs, attn_mask=attn_mask)\n",
    "print(logits.shape)\n",
    "print(scores.shape)"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([2, 50, 12504])\n",
      "torch.Size([2, 50, 50])\n"
     ]
    }
   ],
   "execution_count": 31
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-26T06:56:27.477647Z",
     "start_time": "2025-01-26T06:56:27.474756Z"
    }
   },
   "cell_type": "code",
   "source": [
    "#帮我计算一下model的总参数量\n",
    "def count_parameters(model):\n",
    "    return sum(p.numel() for p in model.parameters() if p.requires_grad)\n",
    "\n",
    "print(f\"The model has {count_parameters(model):,} trainable parameters\")"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The model has 35,212,249 trainable parameters\n"
     ]
    }
   ],
   "execution_count": 32
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-26T06:57:21.077045Z",
     "start_time": "2025-01-26T06:57:20.874745Z"
    }
   },
   "cell_type": "code",
   "source": [
    "#帮我初始化一个4层的GRU的model\n",
    "model=Sequence2Sequence(src_vocab_size=len(src_word2idx), trg_vocab_size=len(trg_word2idx), encoder_num_layers=4, decoder_num_layers=4)\n",
    "print(f\"The model has {count_parameters(model):,} trainable parameters\")"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The model has 72,997,849 trainable parameters\n"
     ]
    }
   ],
   "execution_count": 33
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "zE-vNp-xJzbe"
   },
   "source": [
    "## 训练"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "o55JWSvhJzbe"
   },
   "source": [
    "### 损失函数"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "c_Mmw5GAJzbe",
    "ExecuteTime": {
     "end_time": "2025-01-26T07:30:19.163882Z",
     "start_time": "2025-01-26T07:30:19.160192Z"
    }
   },
   "source": [
    "def cross_entropy_with_padding(logits, labels, padding_mask=None):\n",
    "    # logits.shape = [batch size, sequence length, num of classes]\n",
    "    # labels.shape = [batch size, sequence length]\n",
    "    # padding_mask.shape = [batch size, sequence length] decoder_labels_mask\n",
    "    bs, seq_len, nc = logits.shape\n",
    "    loss = F.cross_entropy(logits.reshape(bs * seq_len, nc), labels.reshape(-1), reduce=False) #reduce=False表示不对batch求平均\n",
    "    if padding_mask is None:#如果没有padding_mask，就直接求平均\n",
    "        loss = loss.mean()\n",
    "    else:\n",
    "        # 如果提供了 padding_mask，则将padding填充部分的损失去除后计算有效损失的均值。首先，通过将 padding_mask reshape 成一维张量，并取 1 减去得到填充掩码。这样填充部分的掩码值变为 1，非填充部分变为 0。将损失张量与填充掩码相乘，这样填充部分的损失就会变为 0。然后，计算非填充部分的损失和（sum）以及非填充部分的掩码数量（sum）作为有效损失的均值计算。(因为上面我们设计的mask的token是0，所以这里是1-padding_mask)\n",
    "        padding_mask = 1 - padding_mask.reshape(-1) #将padding_mask reshape成一维张量，mask部分为0，非mask部分为1\n",
    "        loss = torch.mul(loss, padding_mask).sum() / padding_mask.sum()\n",
    "\n",
    "    return loss\n"
   ],
   "outputs": [],
   "execution_count": 34
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "ITY9VUiiJzbe"
   },
   "source": [
    "### Callback"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "qez1fjOPJzbe",
    "ExecuteTime": {
     "end_time": "2025-01-26T07:30:31.119376Z",
     "start_time": "2025-01-26T07:30:24.728670Z"
    }
   },
   "source": [
    "from torch.utils.tensorboard import SummaryWriter\n",
    "\n",
    "\n",
    "class TensorBoardCallback:\n",
    "    def __init__(self, log_dir, flush_secs=10):\n",
    "        \"\"\"\n",
    "        Args:\n",
    "            log_dir (str): dir to write log.\n",
    "            flush_secs (int, optional): write to dsk each flush_secs seconds. Defaults to 10.\n",
    "        \"\"\"\n",
    "        self.writer = SummaryWriter(log_dir=log_dir, flush_secs=flush_secs)\n",
    "\n",
    "    def draw_model(self, model, input_shape):\n",
    "        self.writer.add_graph(model, input_to_model=torch.randn(input_shape))\n",
    "\n",
    "    def add_loss_scalars(self, step, loss, val_loss):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/loss\",\n",
    "            tag_scalar_dict={\"loss\": loss, \"val_loss\": val_loss},\n",
    "            global_step=step,\n",
    "            )\n",
    "\n",
    "    def add_acc_scalars(self, step, acc, val_acc):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/accuracy\",\n",
    "            tag_scalar_dict={\"accuracy\": acc, \"val_accuracy\": val_acc},\n",
    "            global_step=step,\n",
    "        )\n",
    "\n",
    "    def add_lr_scalars(self, step, learning_rate):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/learning_rate\",\n",
    "            tag_scalar_dict={\"learning_rate\": learning_rate},\n",
    "            global_step=step,\n",
    "\n",
    "        )\n",
    "\n",
    "    def __call__(self, step, **kwargs):\n",
    "        # add loss\n",
    "        loss = kwargs.pop(\"loss\", None)\n",
    "        val_loss = kwargs.pop(\"val_loss\", None)\n",
    "        if loss is not None and val_loss is not None:\n",
    "            self.add_loss_scalars(step, loss, val_loss)\n",
    "        # add acc\n",
    "        acc = kwargs.pop(\"acc\", None)\n",
    "        val_acc = kwargs.pop(\"val_acc\", None)\n",
    "        if acc is not None and val_acc is not None:\n",
    "            self.add_acc_scalars(step, acc, val_acc)\n",
    "        # add lr\n",
    "        learning_rate = kwargs.pop(\"lr\", None)\n",
    "        if learning_rate is not None:\n",
    "            self.add_lr_scalars(step, learning_rate)\n"
   ],
   "outputs": [],
   "execution_count": 35
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "wXtxS8ukJzbe",
    "ExecuteTime": {
     "end_time": "2025-01-26T07:30:39.353371Z",
     "start_time": "2025-01-26T07:30:39.350489Z"
    }
   },
   "source": [
    "class SaveCheckpointsCallback:\n",
    "    def __init__(self, save_dir, save_step=5000, save_best_only=True):\n",
    "        \"\"\"\n",
    "        Save checkpoints each save_epoch epoch.\n",
    "        We save checkpoint by epoch in this implementation.\n",
    "        Usually, training scripts with pytorch evaluating model and save checkpoint by step.\n",
    "\n",
    "        Args:\n",
    "            save_dir (str): dir to save checkpoint\n",
    "            save_epoch (int, optional): the frequency to save checkpoint. Defaults to 1.\n",
    "            save_best_only (bool, optional): If True, only save the best model or save each model at every epoch.\n",
    "        \"\"\"\n",
    "        self.save_dir = save_dir\n",
    "        self.save_step = save_step\n",
    "        self.save_best_only = save_best_only\n",
    "        self.best_metrics = - np.inf\n",
    "\n",
    "        # mkdir\n",
    "        if not os.path.exists(self.save_dir):\n",
    "            os.mkdir(self.save_dir)\n",
    "\n",
    "    def __call__(self, step, state_dict, metric=None):\n",
    "        if step % self.save_step > 0:\n",
    "            return\n",
    "\n",
    "        if self.save_best_only:\n",
    "            assert metric is not None\n",
    "            if metric >= self.best_metrics:\n",
    "                # save checkpoints\n",
    "                torch.save(state_dict, os.path.join(self.save_dir, \"best.ckpt\"))\n",
    "                # update best metrics\n",
    "                self.best_metrics = metric\n",
    "        else:\n",
    "            torch.save(state_dict, os.path.join(self.save_dir, f\"{step}.ckpt\"))\n",
    "\n"
   ],
   "outputs": [],
   "execution_count": 36
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "lfzfWswRJzbe",
    "ExecuteTime": {
     "end_time": "2025-01-26T07:30:43.217883Z",
     "start_time": "2025-01-26T07:30:43.214721Z"
    }
   },
   "source": [
    "class EarlyStopCallback:\n",
    "    def __init__(self, patience=5, min_delta=0.01):\n",
    "        \"\"\"\n",
    "\n",
    "        Args:\n",
    "            patience (int, optional): Number of epochs with no improvement after which training will be stopped.. Defaults to 5.\n",
    "            min_delta (float, optional): Minimum change in the monitored quantity to qualify as an improvement, i.e. an absolute\n",
    "                change of less than min_delta, will count as no improvement. Defaults to 0.01.\n",
    "        \"\"\"\n",
    "        self.patience = patience\n",
    "        self.min_delta = min_delta\n",
    "        self.best_metric = - np.inf\n",
    "        self.counter = 0\n",
    "\n",
    "    def __call__(self, metric):\n",
    "        if metric >= self.best_metric + self.min_delta:\n",
    "            # update best metric\n",
    "            self.best_metric = metric\n",
    "            # reset counter\n",
    "            self.counter = 0\n",
    "        else:\n",
    "            self.counter += 1\n",
    "\n",
    "    @property\n",
    "    def early_stop(self):\n",
    "        return self.counter >= self.patience\n"
   ],
   "outputs": [],
   "execution_count": 37
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "F2f3S6z7Jzbf"
   },
   "source": [
    "### training & valuating"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "IOlJp26YJzbf",
    "ExecuteTime": {
     "end_time": "2025-01-26T07:32:22.636444Z",
     "start_time": "2025-01-26T07:32:22.633639Z"
    }
   },
   "source": [
    "@torch.no_grad()\n",
    "def evaluating(model, dataloader, loss_fct):\n",
    "    loss_list = []\n",
    "    for batch in dataloader:\n",
    "        encoder_inputs = batch[\"encoder_inputs\"]\n",
    "        encoder_inputs_mask = batch[\"encoder_inputs_mask\"]\n",
    "        decoder_inputs = batch[\"decoder_inputs\"]\n",
    "        decoder_labels = batch[\"decoder_labels\"]\n",
    "        decoder_labels_mask = batch[\"decoder_labels_mask\"]\n",
    "\n",
    "        # 前向计算\n",
    "        logits, _ = model(\n",
    "            encoder_inputs=encoder_inputs,\n",
    "            decoder_inputs=decoder_inputs,\n",
    "            attn_mask=encoder_inputs_mask\n",
    "            ) #model就是seq2seq模型\n",
    "        loss = loss_fct(logits, decoder_labels, padding_mask=decoder_labels_mask)         # 验证集损失\n",
    "        loss_list.append(loss.cpu().item())\n",
    "\n",
    "    return np.mean(loss_list)\n"
   ],
   "outputs": [],
   "execution_count": 38
  },
  {
   "cell_type": "code",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 101,
     "referenced_widgets": [
      "267a9f8d838c4649b208914938b1bcff",
      "a9552c62dcb4441fbca47f89e939759d",
      "7dc472c2f73f4443a0e8437074e67476",
      "c46cefacefb54be7ad60ca93855de3ca",
      "9ebbd2d91c9849baaa85cff5b2b048e1",
      "63c6ee4650cb4a938e9f325113e259d1",
      "95d9e57c587f43e6ba5f656f2b2e5c71",
      "863e7ad581894502979884fe0e9e412e",
      "7d21bf5788794ed2b0b4dc4a98a5d2e9",
      "296304e90e43440094ba467cafb20896",
      "c3d8fddae7354c278c8e88291dbcee8d"
     ]
    },
    "id": "brzx2uFHJzbf",
    "outputId": "6c482ef7-5b4c-41e8-954f-6e9d0a034d1b",
    "ExecuteTime": {
     "end_time": "2025-01-26T07:35:03.176551Z",
     "start_time": "2025-01-26T07:35:01.433854Z"
    }
   },
   "source": [
    "# 训练\n",
    "def training(\n",
    "    model,\n",
    "    train_loader,\n",
    "    val_loader,\n",
    "    epoch,\n",
    "    loss_fct,\n",
    "    optimizer,\n",
    "    tensorboard_callback=None,\n",
    "    save_ckpt_callback=None,\n",
    "    early_stop_callback=None,\n",
    "    eval_step=500,\n",
    "    ):\n",
    "    record_dict = {\n",
    "        \"train\": [],\n",
    "        \"val\": []\n",
    "    }\n",
    "\n",
    "    global_step = 1\n",
    "    model.train() # 切换到训练模式\n",
    "    with tqdm(total=epoch * len(train_loader)) as pbar:\n",
    "        for epoch_id in range(epoch):\n",
    "            # training\n",
    "            for batch in train_loader:\n",
    "                encoder_inputs = batch[\"encoder_inputs\"]\n",
    "                encoder_inputs_mask = batch[\"encoder_inputs_mask\"]\n",
    "                decoder_inputs = batch[\"decoder_inputs\"]\n",
    "                decoder_labels = batch[\"decoder_labels\"]\n",
    "                decoder_labels_mask = batch[\"decoder_labels_mask\"]\n",
    "\n",
    "                # 梯度清空\n",
    "                optimizer.zero_grad()\n",
    "\n",
    "                # 前向计算\n",
    "                logits, _ = model(\n",
    "                    encoder_inputs=encoder_inputs,\n",
    "                    decoder_inputs=decoder_inputs,\n",
    "                    attn_mask=encoder_inputs_mask\n",
    "                    )\n",
    "                loss = loss_fct(logits, decoder_labels, padding_mask=decoder_labels_mask)\n",
    "\n",
    "                # 梯度回传\n",
    "                loss.backward()\n",
    "\n",
    "                # 调整优化器，包括学习率的变动等\n",
    "                optimizer.step()\n",
    "\n",
    "                loss = loss.cpu().item()\n",
    "                # record\n",
    "                record_dict[\"train\"].append({\n",
    "                    \"loss\": loss, \"step\": global_step\n",
    "                })\n",
    "\n",
    "                # evaluating\n",
    "                if global_step % eval_step == 0:\n",
    "                    model.eval() # 切换到验证模式\n",
    "                    val_loss = evaluating(model, val_loader, loss_fct)\n",
    "                    record_dict[\"val\"].append({\n",
    "                        \"loss\": val_loss, \"step\": global_step\n",
    "                    })\n",
    "                    model.train() # 切换到训练模式\n",
    "\n",
    "                    # 1. 使用 tensorboard 可视化\n",
    "                    if tensorboard_callback is not None:\n",
    "                        tensorboard_callback(\n",
    "                            global_step,\n",
    "                            loss=loss, val_loss=val_loss,\n",
    "                            lr=optimizer.param_groups[0][\"lr\"],\n",
    "                            )\n",
    "\n",
    "                    # 2. 保存模型权重 save model checkpoint\n",
    "                    if save_ckpt_callback is not None:\n",
    "                        save_ckpt_callback(global_step, model.state_dict(), metric=-val_loss)\n",
    "\n",
    "                    # 3. 早停 Early Stop\n",
    "                    if early_stop_callback is not None:\n",
    "                        early_stop_callback(-val_loss)\n",
    "                        if early_stop_callback.early_stop:\n",
    "                            print(f\"Early stop at epoch {epoch_id} / global_step {global_step}\")\n",
    "                            return record_dict\n",
    "\n",
    "                # udate step\n",
    "                global_step += 1\n",
    "                pbar.update(1)\n",
    "            pbar.set_postfix({\"epoch\": epoch_id, \"loss\": loss, \"val_loss\": val_loss}) # 更新进度条\n",
    "\n",
    "    return record_dict\n",
    "\n",
    "\n",
    "epoch = 20\n",
    "batch_size = 64\n",
    "\n",
    "model = Sequence2Sequence(src_vocab_size=len(src_word2idx), trg_vocab_size=len(trg_word2idx))\n",
    "train_dl = DataLoader(train_ds, batch_size=batch_size, shuffle=True, collate_fn=collate_fct)\n",
    "test_dl = DataLoader(test_ds, batch_size=batch_size, shuffle=False, collate_fn=collate_fct)\n",
    "\n",
    "# 1. 定义损失函数 采用交叉熵损失\n",
    "loss_fct = cross_entropy_with_padding\n",
    "# 2. 定义优化器 采用 adam\n",
    "# Optimizers specified in the torch.optim package\n",
    "optimizer = torch.optim.Adam(model.parameters(), lr=0.001)\n",
    "\n",
    "# 1. tensorboard 可视化\n",
    "if not os.path.exists(\"runs\"):\n",
    "    os.mkdir(\"runs\")\n",
    "exp_name = \"translate-seq2seq\"\n",
    "tensorboard_callback = TensorBoardCallback(f\"runs/{exp_name}\")\n",
    "# tensorboard_callback.draw_model(model, [1, MAX_LENGTH])\n",
    "# 2. save best\n",
    "if not os.path.exists(\"checkpoints\"):\n",
    "    os.makedirs(\"checkpoints\")\n",
    "save_ckpt_callback = SaveCheckpointsCallback(\n",
    "    f\"checkpoints/{exp_name}\", save_step=200, save_best_only=True)\n",
    "# 3. early stop\n",
    "early_stop_callback = EarlyStopCallback(patience=5)\n",
    "\n",
    "model = model.to(device)\n",
    "\n"
   ],
   "outputs": [],
   "execution_count": 39
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": [
    "record = training(\n",
    "    model,\n",
    "    train_dl,\n",
    "    test_dl,\n",
    "    epoch,\n",
    "    loss_fct,\n",
    "    optimizer,\n",
    "    tensorboard_callback=None,\n",
    "    save_ckpt_callback=save_ckpt_callback,\n",
    "    early_stop_callback=early_stop_callback,\n",
    "    eval_step=200\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "outputs": [
    {
     "data": {
      "text/plain": "35212249"
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#计算模型参数量\n",
    "sum(i[1].numel() for i in model.named_parameters())"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-08-01T07:21:09.603023900Z",
     "start_time": "2024-08-01T07:21:09.587018500Z"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "outputs": [
    {
     "data": {
      "text/plain": "1676.0"
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "33520/20"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-08-01T06:51:34.906600900Z",
     "start_time": "2024-08-01T06:51:34.891495800Z"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "outputs": [
    {
     "data": {
      "text/plain": "1672.93125"
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "118964*0.9/64"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-08-01T06:50:49.447830800Z",
     "start_time": "2024-08-01T06:50:49.438888Z"
    }
   }
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": "record[\"train\"][-1]"
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 430
    },
    "id": "mAKeaApNJzbf",
    "outputId": "2933f0b5-29b1-47bc-a6dd-63350da513ca"
   },
   "outputs": [
    {
     "output_type": "display_data",
     "data": {
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ],
      "image/png": "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g5CQEI/nVStY6HQ69OzZE1u2bLEfe+yxx7Bz505s3brV7XPctVjEx8fj0qVLFRasulq9sMp++/irw7x23frIaDRi9erVGDp0KLRardzFqXW+VF/WVbl8qb6sqzLl5uYiKiqq0mBRra6QRo0aoX379k7H2rVrh19++cXjc/R6PfR6vctxrVZba78Epf9ybWrz77A+8qX6sq7K5Uv1ZV2Vpar1q9Z00/79++Po0aNOx44dO4amTZtW5zJERESkUNUKFo8//ji2bduGN954A8nJyViwYAG++OILTJo0qbbKV2U3d24kdxGIiIh8XrWCRa9evfDbb79h4cKF6NixI1599VXMmjULY8aMqa3yVVliM+taGoPbNpC5JERERL6rWmMsAODGG2/EjTfeWBtluSK2vUIsXMiCiIhINsrZK6Q0WViYK4iIiGSjmGChsu1uyhYLIiIi2SgoWJTuFcJcQUREJBvFBAvbXiHsCiEiIpKPcoKFvcWCyYKIiEguigkWKs4KISIikp2CgkVpi4XM5SAiIvJligkWBpMFALD9VJbMJSEiIvJdigkWyw+lyV0EIiIin6eYYFFiYicIERGR3BQTLEL8qr06OREREXmZYoLF+AEJcheBiIjI5ykmWIQFaAEAATq1zCUhIiLyXYoJFurShSwKS8wyl4SIiMh3KSZY2NaxAIBiI8MFERGRHBQTLMwOm4ScuVwoY0mIiIh8l2KChXBYc/PrTadkLAkREZHvUkywCPPX2m9fyC2WsSRERES+SzHBItivLFhoVFIFZxIREVFtUUywcBSo52JZREREclBksGB7BRERkTwUGSy4awgREZE8FBks/jnDrdOJiIjkoMhgcT67SO4iEBER+SRFBgsAOHO5QO4iEBER+RzFBoufdp2TuwhEREQ+R7HBgktZEBER1T3FBgtJYrIgIiKqawoOFnKXgIiIyPcoNlj8uDNF7iIQERH5HMUGi9ScYhhMZrmLQURE5FMUFSyuj7U43R//zS6ZSkJEROSbFBUsNOXGVfx9/JI8BSEiIvJRygoWKu4SQkREJCdFBYueUQwWREREclJUsIj0k7sEREREvk1RwYKIiIjkxWBBREREXqO4YNG/RaTcRSAiIvJZigsWzaMC5C4CERGRz1JcsLBwYggREZFsFBcs+jaPcLr/x/4LMpWEiIjI9yguWAxrH+10f9KCf2Qq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\n"
     },
     "metadata": {}
    }
   ],
   "source": [
    "plt.plot([i[\"step\"] for i in record[\"train\"]], [i[\"loss\"] for i in record[\"train\"]], label=\"train\")\n",
    "plt.plot([i[\"step\"] for i in record[\"val\"]], [i[\"loss\"] for i in record[\"val\"]], label=\"val\")\n",
    "plt.grid()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "UOSROA66Jzbg"
   },
   "source": [
    "## 推理\n",
    "\n",
    "- 接下来进行翻译推理，并作出注意力的热度图"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 875
    },
    "id": "cX75BqcBJzbg",
    "outputId": "e9dcbb2c-5188-4f2e-a1ac-793fe5f40b19",
    "ExecuteTime": {
     "end_time": "2025-01-26T07:44:43.239054Z",
     "start_time": "2025-01-26T07:44:43.106128Z"
    }
   },
   "source": [
    "# load checkpoints,如何上线\n",
    "model = Sequence2Sequence(len(src_word2idx), len(trg_word2idx))\n",
    "model.load_state_dict(torch.load(f\"./best.ckpt\", weights_only=True,map_location=\"cpu\"))\n",
    "\n",
    "class Translator:\n",
    "    def __init__(self, model, src_tokenizer, trg_tokenizer):\n",
    "        self.model = model\n",
    "        self.model.eval() # 切换到验证模式\n",
    "        self.src_tokenizer = src_tokenizer\n",
    "        self.trg_tokenizer = trg_tokenizer\n",
    "\n",
    "    def draw_attention_map(self, scores, src_words_list, trg_words_list):\n",
    "        \"\"\"绘制注意力热力图\n",
    "\n",
    "        Args:\n",
    "            - scores (numpy.ndarray): shape = [source sequence length, target sequence length]\n",
    "        \"\"\"\n",
    "        plt.matshow(scores.T, cmap='viridis') # 注意力矩阵,显示注意力分数值\n",
    "        # 获取当前的轴\n",
    "        ax = plt.gca()\n",
    "\n",
    "        # 设置热图中每个单元格的分数的文本\n",
    "        for i in range(scores.shape[0]): #输入\n",
    "            for j in range(scores.shape[1]): #输出\n",
    "                ax.text(j, i, f'{scores[i, j]:.2f}',  # 格式化数字显示\n",
    "                               ha='center', va='center', color='k')\n",
    "\n",
    "        plt.xticks(range(scores.shape[0]), src_words_list)\n",
    "        plt.yticks(range(scores.shape[1]), trg_words_list)\n",
    "        plt.show()\n",
    "\n",
    "    def __call__(self, sentence):\n",
    "        sentence = preprocess_sentence(sentence) # 预处理句子，标点符号处理等\n",
    "        encoder_input, attn_mask = self.src_tokenizer.encode(\n",
    "            [sentence.split()],\n",
    "            padding_first=True,\n",
    "            add_bos=True,\n",
    "            add_eos=True,\n",
    "            return_mask=True,\n",
    "            ) # 对输入进行编码，并返回encode_piadding_mask\n",
    "        encoder_input = torch.Tensor(encoder_input).to(dtype=torch.int64) # 转换成tensor\n",
    "\n",
    "        preds, scores = model.infer(encoder_input=encoder_input, attn_mask=attn_mask) #预测\n",
    "\n",
    "        trg_sentence = self.trg_tokenizer.decode([preds], split=True, remove_eos=False)[0] #通过tokenizer转换成文字\n",
    "\n",
    "        src_decoded = self.src_tokenizer.decode(\n",
    "            encoder_input.tolist(),\n",
    "            split=True,\n",
    "            remove_bos=False,\n",
    "            remove_eos=False\n",
    "            )[0] #对输入编码id进行解码，转换成文字,为了画图\n",
    "\n",
    "        self.draw_attention_map(\n",
    "            scores.squeeze(0).numpy(),\n",
    "            src_decoded, # 注意力图的源句子\n",
    "            trg_sentence # 注意力图的目标句子\n",
    "            )\n",
    "        return \" \".join(trg_sentence[:-1])\n",
    "\n",
    "\n"
   ],
   "outputs": [],
   "execution_count": 41
  },
  {
   "cell_type": "code",
   "source": [
    "translator = Translator(model.cpu(), src_tokenizer, trg_tokenizer)\n",
    "translator(u'hace mucho frio aqui .')"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-01-26T07:44:47.097660Z",
     "start_time": "2025-01-26T07:44:46.943948Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<Figure size 480x480 with 1 Axes>"
      ],
      "image/png": "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"
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "'it s very cold here .'"
      ]
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 42
  },
  {
   "cell_type": "code",
   "source": "translator(u'Hoy es un buen día.')",
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-01-26T07:46:57.181444Z",
     "start_time": "2025-01-26T07:46:57.099699Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<Figure size 548.571x480 with 1 Axes>"
      ],
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAe4AAAHBCAYAAACv7Vi1AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAhTtJREFUeJzt3Qd8G/X5P/DPaVvL8t4ry47jDGcnZEECAcootEApLdAySid0/SkdjNJf6aC/0v0rHZRCgZay9wwhZO/ESRyP2PHeU5a17/96vopkyZFDEmLFJ57363XEuvvqrMd3uue+65BkWZbBGGOMMUVQne0PwBhjjLGTx4mbMcYYUxBO3IwxxpiCcOJmjDHGFIQTN2OMMaYgnLgZY4wxBeHEzRhjjCkIJ27GGGNMQThxM8YYYwrCiTvMqlWrIEmSWPbs2XNWPkN9fX3oM8yZMycmMd9xxx3j/nvYxxufZ8f/HQoLC/HQQw+d7Y8U11ZNgGv6eHwGTtyj3HLLLWhtbUVZWVlEEqVFp9NhypQp+MlPfoLRT4o9cOAArr76aqSlpUGv12PatGm4++674XA4Isrt3bsXl112GdLT02EwGMSX95prrkFHR4fYnpeXJ37/t7/97ZjGzRiLre3bt+PWW2892x/jY31Nl8KWLVu2hN4zPDyMe+65R1zH6XqempqKq666Slznw9H1/a677sLkyZPF9Zyu/ytXrsQLL7wQKvPss89i27ZtZzQmzRndWxwwGo3IzMyMWPf2229jxowZcLlc+OCDD3DzzTcjKysLN910k9hOB3zNmjVieeWVV5CRkSEOFCXfd955B+vWrRNJv7OzE6tXr8Yll1yCN954AzabTZxIL774IoaGhsS+1Gq1+P1ms/msxM8Yiw26yLOze00Pl5KSIv6l6zxdyxsaGvCrX/0KixYtQnt7Ox544AHxM7138eLFouxtt92GrVu34ne/+x1KS0vR3d2NTZs2iX+DkpOTMTAwgDOJa9wngQ4oHfiCggJcd911OOecc7Br1y6xjWrelMCnT58u7qwWLlwoytHd2UsvvYTNmzfj17/+tSi7ceNG9Pf3469//SvKy8tRVFSEc889V2ynn88Wv9+P//f//p84wSjOe++9N7SNTt7LL79c3EhYrVbRqkAnMaGbDpVKhR07dkTsj5r/6G9A+50o6LPQF4/+zgkJCZg9ezb++9//im29vb3iuNKFlLZNnToVjzzyCCayaM2s1LUSPHZUg6Dz7IorrhAXLoqJbhDPJq/Xi6997WtITEwUNZgf/ehHoZYr+rzPP/98RHm6sf3HP/4Ret3Y2CjOP1pP5yqdl3QOBt1444345Cc/iQcffFDcWNP39qtf/So8Hg/OBroZv/7668V3hz4PJYETHcP//d//xcyZM2EymUTL21e+8hXY7faz8Mk/Ptf0zLBFq9WKbXRM6Lr98ssvi/ONrmV0XX/mmWfEdZ6u98Hzlr5T3//+93HxxReL4zlv3jx8/etfxxe/+MVx/fycuE8RJamdO3eKOy9CfRYHDx7Et771LZHEwlFyoDu3J598Urymk4MuXs8999xxTe1n06OPPiouFnTn+Itf/AI//vGP8dZbb4lkRxfHnp4erF+/Xqw7cuSIaNondKJSfKOTHL2mi+jov8fZREn7n//8J/7v//5PNHd985vfxOc+9zkRFyUQOoavvfYaDh06hD/96U8isSjdfffdJy48+/btExcWujmhY3k2zzONRiNao37zm9+IREU3FyeDku/atWthsViwYcMGcRNMCfHCCy+E2+0OlaPWrdraWvEv/T5K/OHJP5a++93vivOLmk3ffPNNvPfee6Eb/mjo+/Lb3/5WnJ/02d99911xQ81i64knnsD5558vrt+jjw9dN+haQV2ewWv6q6++isHBwdh+SPrferKAlStXyrfffnvodV1dHWVXOSEhQTaZTLJWqxWvb7311lCZp556SqzbvXt31H1+4xvfEO8P+v73vy9rNBo5OTlZvvDCC+Vf/OIXcltb23Hvu+eee+TZs2fLsYh52bJlEesWLFgg33nnnfKbb74pq9VquaGhIbTtwIEDIt5t27aJ1//+97/lpKQk2el0itc7d+6UJUkSf7uJgj6b0WiUN23aFLH+pptukq+99lr50ksvlb/whS/ISlJQUCD/+te/jlhH5wudN4SO0Q9/+MPQNrvdLta99tpr8tlA59n06dNlv98fWkfnGK0Lft7nnnsu4j2JiYnyI488In5+7LHH5OLi4oj3u1wu8d164403xOsbbrhB/F28Xm+ozFVXXSVfc801cqwNDg7KOp1O/s9//hNa193dLT5v8BoT7RiGe/rpp+WUlJSYfN6P6zXdFLYEGQyGiPeE27Vrl3g/XffI+vXr5dzcXJEb5s+f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"
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "'today is a good day .'"
      ]
     },
     "execution_count": 44,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 44
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": [
    "model = Sequence2Sequence(len(src_word2idx), len(trg_word2idx))\n",
    "model.load_state_dict(torch.load(f\"./checkpoints/translate-seq2seq/best.ckpt\", map_location=\"cpu\"))\n",
    "\n",
    "class Translator:\n",
    "    def __init__(self, model, src_tokenizer, trg_tokenizer):\n",
    "        self.model = model\n",
    "        self.model.eval() # 切换到验证模式\n",
    "        self.src_tokenizer = src_tokenizer\n",
    "        self.trg_tokenizer = trg_tokenizer\n",
    "\n",
    "    def __call__(self, sentence):\n",
    "        sentence = preprocess_sentence(sentence) # 预处理句子，标点符号处理等\n",
    "        encoder_input, attn_mask = self.src_tokenizer.encode(\n",
    "            [sentence.split()],\n",
    "            padding_first=True,\n",
    "            add_bos=True,\n",
    "            add_eos=True,\n",
    "            return_mask=True,\n",
    "            ) # 对输入进行编码，并返回encode_piadding_mask\n",
    "        encoder_input = torch.Tensor(encoder_input).to(dtype=torch.int64) # 转换成tensor\n",
    "\n",
    "        preds, scores = model.infer(encoder_input=encoder_input, attn_mask=attn_mask) #预测\n",
    "\n",
    "        trg_sentence = self.trg_tokenizer.decode([preds], split=True, remove_eos=False)[0] #通过tokenizer转换成文字\n",
    "\n",
    "        return \" \".join(trg_sentence[:-1])\n",
    "\n",
    "from nltk.translate.bleu_score import sentence_bleu\n",
    "\n",
    "def evaluate_bleu_on_test_set(test_data, translator):\n",
    "    \"\"\"\n",
    "    在测试集上计算平均 BLEU 分数。\n",
    "    :param test_data: 测试集数据，格式为 [(src_sentence, [ref_translation1, ref_translation2, ...]), ...]\n",
    "    :param translator: 翻译器对象（Translator 类的实例）\n",
    "    :return: 平均 BLEU 分数\n",
    "    \"\"\"\n",
    "    total_bleu = 0.0\n",
    "    num_samples = len(test_data)\n",
    "    i=0\n",
    "    for src_sentence, ref_translations in test_data:\n",
    "        # 使用翻译器生成翻译结果\n",
    "        candidate_translation = translator(src_sentence)\n",
    "\n",
    "        # 计算 BLEU 分数\n",
    "        bleu_score = sentence_bleu([ref_translations.split()], candidate_translation.split(),weights=(1, 0, 0, 0))\n",
    "        total_bleu += bleu_score\n",
    "\n",
    "        # 打印当前句子的 BLEU 分数（可选）\n",
    "        # print(f\"Source: {src_sentence}\")\n",
    "        # print(f\"Reference: {ref_translations}\")\n",
    "        # print(f\"Candidate: {candidate_translation}\")\n",
    "        # print(f\"BLEU: {bleu_score:.4f}\")\n",
    "        # print(\"-\" * 50)\n",
    "        # i+=1\n",
    "        # if i>10:\n",
    "        #     break\n",
    "    # 计算平均 BLEU 分数\n",
    "    avg_bleu = total_bleu / num_samples\n",
    "    return avg_bleu\n",
    "translator = Translator(model.cpu(), src_tokenizer, trg_tokenizer)\n",
    "evaluate_bleu_on_test_set(test_ds, translator)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "name": "python3",
   "language": "python",
   "display_name": "Python 3 (ipykernel)"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.8"
  },
  "orig_nbformat": 4,
  "colab": {
   "provenance": [],
   "gpuType": "V100"
  },
  "accelerator": "GPU",
  "widgets": {
   "application/vnd.jupyter.widget-state+json": {
    "267a9f8d838c4649b208914938b1bcff": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HBoxModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HBoxModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HBoxView",
      "box_style": "",
      "children": [
       "IPY_MODEL_a9552c62dcb4441fbca47f89e939759d",
       "IPY_MODEL_7dc472c2f73f4443a0e8437074e67476",
       "IPY_MODEL_c46cefacefb54be7ad60ca93855de3ca"
      ],
      "layout": "IPY_MODEL_9ebbd2d91c9849baaa85cff5b2b048e1"
     }
    },
    "a9552c62dcb4441fbca47f89e939759d": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HTMLModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HTMLModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HTMLView",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_63c6ee4650cb4a938e9f325113e259d1",
      "placeholder": "​",
      "style": "IPY_MODEL_95d9e57c587f43e6ba5f656f2b2e5c71",
      "value": " 23%"
     }
    },
    "7dc472c2f73f4443a0e8437074e67476": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "FloatProgressModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "FloatProgressModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "ProgressView",
      "bar_style": "danger",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_863e7ad581894502979884fe0e9e412e",
      "max": 33420,
      "min": 0,
      "orientation": "horizontal",
      "style": "IPY_MODEL_7d21bf5788794ed2b0b4dc4a98a5d2e9",
      "value": 7599
     }
    },
    "c46cefacefb54be7ad60ca93855de3ca": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HTMLModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HTMLModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HTMLView",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_296304e90e43440094ba467cafb20896",
      "placeholder": "​",
      "style": "IPY_MODEL_c3d8fddae7354c278c8e88291dbcee8d",
      "value": " 7599/33420 [09:31&lt;23:51, 18.03it/s, epoch=3, loss=1.2, val_loss=1.26]"
     }
    },
    "9ebbd2d91c9849baaa85cff5b2b048e1": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "63c6ee4650cb4a938e9f325113e259d1": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "95d9e57c587f43e6ba5f656f2b2e5c71": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "DescriptionStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "DescriptionStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "description_width": ""
     }
    },
    "863e7ad581894502979884fe0e9e412e": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "7d21bf5788794ed2b0b4dc4a98a5d2e9": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "ProgressStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "ProgressStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "bar_color": null,
      "description_width": ""
     }
    },
    "296304e90e43440094ba467cafb20896": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "c3d8fddae7354c278c8e88291dbcee8d": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "DescriptionStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "DescriptionStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "description_width": ""
     }
    }
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
